#ifndef COCKPITVARIOMETER_H
#define COCKPITVARIOMETER_H

#include "CockpitBar.h"

namespace VariometerUtils {
    template <typename Float>
    struct VerticalSpeedSlide
    {
        Float vertical_speed, slide;
    };

    /** 
        @brief Вычисляет процентное отношение параметра относительно его минимума и максимума
        @return Значение от 0 (если параметр == минимум) до 1 (параметр == максимум)
    */
    template <typename Float>
    Float valueModifier(Float value, Float min, Float max)
    {
        return (value - min) / (max - min);
    }

    template <typename Float>
    Float modifierToValue(Float value, Float min, Float max)
    {
        return value * (max - min) + min;
    }
}

template <typename Float>
class CockpitVariometer : public CockpitBar<VariometerUtils::VerticalSpeedSlide<Float>>
{
public:
    CockpitVariometer(VariometerUtils::VerticalSpeedSlide<Float> value)
    : CockpitBar<VariometerUtils::VerticalSpeedSlide<Float>>(value
                        , [](const VariometerUtils::VerticalSpeedSlide<Float> & value) -> VariometerUtils::VerticalSpeedSlide<Float>
                        {
                            return { value.vertical_speed
                                    , std::clamp(value.slide, _MIN_SLIDE, _MAX_SLIDE)
                                    };
                        }
                        )
    {}

protected:
    void paintEvent(QPaintEvent * event) override
    {
        CockpitBar<VariometerUtils::VerticalSpeedSlide<Float>>::paintEvent(event);

        QPainter painter(this);
        painter.setFont(QFont("monospace"));

        _drawVerticalSpeedIndicator(painter);
        _drawSlideIndicator(painter);
    }

private:
    static constexpr Float _MAX_VERTICAL_SPEED = 100;
    static constexpr Float _MIN_VERTICAL_SPEED = -_MAX_VERTICAL_SPEED;
    static constexpr Float _MAX_SLIDE = 15;
    static constexpr Float _MIN_SLIDE = -_MAX_SLIDE;
    static constexpr Float _SIDE_PADDING_MULTIPLIER = 1.0 / 20;
    // static const QString _TEXT_FONT_FAMILY;

    void _drawVerticalSpeedIndicator(QPainter & painter)
    {
        const QPen black_pen_2(Qt::black, 2);
        const QPen red_pen_4(Qt::red, 4);
        const QBrush cyan_brush(Qt::cyan);

        const QPointF center = CockpitBar<VariometerUtils::VerticalSpeedSlide<Float>>::center();
        const Float min_side = std::min(this->width(), this->height());
        const Float side_size = min_side * (1 - 2 * _SIDE_PADDING_MULTIPLIER);
        const QRectF indicator_rect = { center.x() - side_size / 2
                                        , center.y() - side_size / 2
                                        , side_size
                                        , side_size
                                        };

        painter.setPen(black_pen_2);
        painter.setBrush(cyan_brush);
        painter.drawPie(indicator_rect, 90 * 16, 180 * 16);

        const Float side_size_2 = side_size / 2.;
        const Float side_size_4 = side_size / 4.;
        const Float side_size_8 = side_size / 8.;
        const Float side_size_16 = side_size / 16.;

        const Float vertical_speed = std::round(this->value().vertical_speed * 10.0) / 10.0;
        const Float clamped_vertical_speed = std::clamp(vertical_speed, _MIN_VERTICAL_SPEED, _MAX_VERTICAL_SPEED);
        const Float angle_radians = VariometerUtils::valueModifier(clamped_vertical_speed, _MIN_VERTICAL_SPEED, _MAX_VERTICAL_SPEED) * M_PI - M_PI_2;
        
        const QPointF arrow_point = { center.x() - std::cos(angle_radians) * 3 * side_size_8
                                    , center.y() - std::sin(angle_radians) * 3 * side_size_8
                                    };
        const QRectF center_rect = { center.x() - side_size_16
                                    , center.y() - side_size_16
                                    , side_size_8
                                    , side_size_8
                                    };
        
        const Float step = 20;
        for (Float vertical_speed = _MIN_VERTICAL_SPEED + step; vertical_speed <= _MAX_VERTICAL_SPEED - step; vertical_speed += step)
        {
            const Float angle_radians = VariometerUtils::valueModifier(vertical_speed, _MIN_VERTICAL_SPEED, _MAX_VERTICAL_SPEED) * M_PI - M_PI_2;
            const QPointF vs_arrow_point = { center.x() - std::cos(angle_radians) * side_size_2
                                                , center.y() - std::sin(angle_radians) * side_size_2
                                                };
            const QPointF vs_mirror_point = { center.x() - std::cos(angle_radians) * 3 * side_size_8
                                                , center.y() - std::sin(angle_radians) * 3 * side_size_8
                                                };
            const QLineF line(vs_arrow_point, vs_mirror_point);
            const Float modified_line_length = line.length() / 2.;
            const QRectF vs_text_rect = { QPointF{ line.center().x() - modified_line_length
                                                    , line.center().y() - modified_line_length
                                                    }
                                            , QPointF{ line.center().x() + modified_line_length
                                                        , line.center().y() + modified_line_length
                                                        }
                                            };
            const QString vs_text_string = QString::number(std::round(vertical_speed));

            QString string_to_fit = vs_text_string;
            while (string_to_fit.size() < 3)
            {
                string_to_fit = "-" + string_to_fit;
            }

            painter.setFont(CockpitBarUtils::fitTextToRectF(string_to_fit, painter.font(), vs_text_rect));
            painter.drawText(vs_text_rect, Qt::AlignCenter, vs_text_string);
        }
        
        painter.setPen(red_pen_4);
        painter.drawLine(arrow_point, center);
        painter.setPen(black_pen_2);
        painter.drawEllipse(center_rect);

        QString vs_string = QString::number(vertical_speed);
        const int dot_position = vs_string.size() - 2;
        if (dot_position < 1 || vs_string.at(dot_position) != '.')
        {
            vs_string += ".0";
        }
        if (0 < vertical_speed)
        {
            vs_string.prepend(" ");
        }
        else if (!(vertical_speed < 0))
        {
            vs_string.prepend(this->value().vertical_speed < 0 ? "-" : " ");
        }
        const QString vertical_speed_string = vs_string;

        const QRectF number_indicator_rect = { center.x() + side_size_8 / 2
                                                , center.y() - 3 * side_size_8
                                                , 3 * side_size_8
                                                , side_size_4 };

        painter.setPen(black_pen_2);
        painter.drawRect(number_indicator_rect);
        painter.setFont(CockpitBarUtils::fitTextToRectF(vertical_speed_string, painter.font(), number_indicator_rect));
        painter.drawText(number_indicator_rect, Qt::AlignCenter, vertical_speed_string);
    }

    void _drawSlideIndicator(QPainter & painter)
    {
        const QPen black_pen_2(Qt::black, 2);
        const QPen red_pen_4(Qt::red, 4);

        const QPointF center = CockpitBar<VariometerUtils::VerticalSpeedSlide<Float>>::center();
        const Float min_side = std::min(this->width(), this->height());
        const Float side_size = min_side * (1 - 2 * _SIDE_PADDING_MULTIPLIER);

        const Float side_size_8 = side_size / 8.;

        const Float slide = std::round(this->value().slide);
        const QRectF slide_indicator_rect = { center.x() + side_size_8 / 2.
                                            , center.y() + 3. * side_size_8 / 2.
                                            , 3. * side_size_8
                                            , side_size_8
                                            };
        const QLineF zero_slide_indicator_line = { slide_indicator_rect.x() + slide_indicator_rect.width() / 2.
                                                    , slide_indicator_rect.y()
                                                    , slide_indicator_rect.x() + slide_indicator_rect.width() / 2.
                                                    , slide_indicator_rect.y() + slide_indicator_rect.height()
                                                    };
        const Float slide_modifier = VariometerUtils::valueModifier(slide, _MIN_SLIDE, _MAX_SLIDE) * 2 -  1;
        const QLineF slide_indicator_line = { zero_slide_indicator_line.x1() + slide_modifier * slide_indicator_rect.width() / 2.
                                                    , zero_slide_indicator_line.y1()
                                                    , zero_slide_indicator_line.x1() + slide_modifier * slide_indicator_rect.width() / 2.
                                                    , zero_slide_indicator_line.y2()
                                                    };

        painter.drawRect(slide_indicator_rect);
        painter.drawLine(zero_slide_indicator_line);
        painter.setPen(red_pen_4);
        painter.drawLine(slide_indicator_line);
        
        /// @todo Отрефакторить
        const QRectF slide_min_rect = { slide_indicator_rect.x()
                                            , slide_indicator_rect.y() - slide_indicator_rect.height()
                                            , slide_indicator_rect.width() / 2.
                                            , slide_indicator_rect.height()
                                            };
        const QString slide_min_string = QString::number(_MIN_SLIDE);
        const QRectF slide_max_rect = { slide_indicator_rect.x() + slide_min_rect.width()
                                            , slide_indicator_rect.y() + slide_indicator_rect.height()
                                            , slide_min_rect.width()
                                            , slide_min_rect.height()
                                            };
        const QString slide_max_string = QString::number(_MAX_SLIDE);
        
        
        painter.setPen(black_pen_2);
        painter.setFont(CockpitBarUtils::fitTextToRectF(slide_min_string, painter.font(), slide_min_rect));
        painter.drawText(slide_min_rect
                        , Qt::AlignLeft | Qt::AlignVCenter
                        , slide_min_string
                        );
        painter.setFont(CockpitBarUtils::fitTextToRectF(slide_max_string, painter.font(), slide_max_rect));
        painter.drawText(slide_max_rect
                        , Qt::AlignRight | Qt::AlignVCenter
                        , slide_max_string
                        );
    }
};

#endif // COCKPITVARIOMETER_H
